package com.luych.toolbox.docker.service.service;

import com.luych.toolbox.common.enums.CssColor;
import com.luych.toolbox.common.enums.DockerContainerStatus;
import com.luych.toolbox.common.enums.TaskStatus;
import com.luych.toolbox.common.enums.TaskType;
import com.luych.toolbox.common.event.base.service.publisher.WebSocketEventPublisher;
import com.luych.toolbox.common.feign.intf.base.service.TaskFeign;
import com.luych.toolbox.common.feign.param.base.service.TaskParam;
import com.luych.toolbox.common.feign.param.docker.service.DockerContainerParam;
import com.luych.toolbox.common.feign.param.gateway.center.WebSocketEvent;
import com.luych.toolbox.common.feign.view.base.service.TaskView;
import com.luych.toolbox.common.feign.view.base.service.UserView;
import com.luych.toolbox.common.feign.view.docker.service.DockerContainerView;
import com.luych.toolbox.common.feign.view.docker.service.DockerStatDonutView;
import com.luych.toolbox.common.feign.view.docker.service.DockerStatLineView;
import com.luych.toolbox.common.page.CommonPageResult;
import com.luych.toolbox.common.service.exception.HttpException;
import com.luych.toolbox.common.service.security.SecurityUtil;
import com.luych.toolbox.common.service.tool.Capacity;
import com.luych.toolbox.docker.service.constant.Constant;
import com.luych.toolbox.docker.service.dao.DockerServerDao;
import com.luych.toolbox.docker.service.dao.DockerStatDao;
import com.luych.toolbox.docker.service.entity.DockerServer;
import com.luych.toolbox.docker.service.entity.DockerStat;
import com.luych.toolbox.docker.service.job.DockerContainerRestartJob;
import com.luych.toolbox.docker.service.job.DockerContainerStopJob;
import com.luych.toolbox.docker.service.sdk.DockerClient;
import com.luych.toolbox.docker.service.sdk.container.model.DockerContainerCreateParam;
import com.luych.toolbox.docker.service.sdk.container.model.DockerContainerCreateResult;
import com.luych.toolbox.docker.service.sdk.container.model.DockerContainerInspectResult;
import com.luych.toolbox.docker.service.sdk.container.model.DockerContainersResult;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DockerContainerService {

    private static final Logger logger = LoggerFactory.getLogger(DockerContainerService.class);

    private final Scheduler scheduler;
    private final DockerServerDao dockerServerDao;
    private final DockerStatDao dockerStatDao;
    private final TaskFeign taskFeign;
    private final WebSocketEventPublisher eventPublisher;

    @Autowired
    public DockerContainerService(Scheduler scheduler, DockerServerDao dockerServerDao, DockerStatDao dockerStatDao, TaskFeign taskFeign, WebSocketEventPublisher eventPublisher) {
        this.scheduler = scheduler;
        this.dockerServerDao = dockerServerDao;
        this.dockerStatDao = dockerStatDao;
        this.taskFeign = taskFeign;
        this.eventPublisher = eventPublisher;
    }

    private DockerContainerView convert(DockerContainerInspectResult result) {
        DockerContainerView view = new DockerContainerView();
        view.setId(result.getId());
        view.setName(result.getName());
        view.setCreated(result.getCreatedDate());
        view.setCpu(result.getHostConfig().getNanoCpus());
        view.setMem(result.getHostConfig().getMemory());
        view.setMemHumanRead(Capacity.convertHumanReadSimple(result.getHostConfig().getMemory()));
        view.setPath(result.getPath());
        view.setArgs(result.getArgs());
        view.setImageId(result.getImage());
        view.setImageName(result.getConfig().getImage());
        view.setStatus(DockerContainerStatus.valueOf(result.getState().getStatus().toUpperCase()));
        view.setRestartPolicy(result.getHostConfig().getRestartPolicy().getName());
        view.setMaxRestartCount(result.getHostConfig().getRestartPolicy().getMaximumRetryCount());
        view.setRestartCount(result.getRestartCount());
        view.setPid(result.getState().getPid());
        view.setExitCode(result.getState().getExitCode());
        view.setAutoRemove(result.getHostConfig().getAutoRemove());
        view.setPrivileged(result.getHostConfig().getPrivileged());
        view.setError(result.getState().getError());
        view.setStartedAt(result.getState().getStartedAtDate());
        view.setFinishedAt(result.getState().getFinishedAtDate());
        view.setHostname(result.getConfig().getHostname());
        view.setUser(result.getConfig().getUser());
        view.setEnv(result.getConfig().getEnv());
        view.setLabel(result.getConfig().getLabels());
        view.setDns(result.getHostConfig().getDns());
        view.setHosts(result.getHostConfig().getExtraHosts());

        if (result.getMounts() != null) {
            view.setVolumes(new ArrayList<>());
            for (DockerContainerInspectResult.Mount dockerContainerResultMount : result.getMounts()) {
                DockerContainerView.Volume dockerContainerVolumeView = new DockerContainerView.Volume();
                dockerContainerVolumeView.setSource(dockerContainerResultMount.getSource());
                dockerContainerVolumeView.setDestination(dockerContainerResultMount.getDestination());
                dockerContainerVolumeView.setMode(dockerContainerResultMount.getMode());
                view.getVolumes().add(dockerContainerVolumeView);
            }

        }
        if (result.getNetworkSettings() != null && result.getNetworkSettings().getNetworks() != null) {
            view.setNetworks(new ArrayList<>());
            for (Map.Entry<String, DockerContainerInspectResult.NetworkSettings.Network> dockerContainerNetworkMount : result.getNetworkSettings().getNetworks().entrySet()) {
                DockerContainerView.Network dockerContainerNetworkView = new DockerContainerView.Network();
                dockerContainerNetworkView.setNetworkId(dockerContainerNetworkMount.getValue().getNetworkID());
                dockerContainerNetworkView.setNetworkName(dockerContainerNetworkMount.getKey());
                dockerContainerNetworkView.setIpAddress(dockerContainerNetworkMount.getValue().getiPAddress());
                dockerContainerNetworkView.setMacAddress(dockerContainerNetworkMount.getValue().getMacAddress());
                dockerContainerNetworkView.setIpPrefixLen(dockerContainerNetworkMount.getValue().getiPPrefixLen());
                dockerContainerNetworkView.setGateway(dockerContainerNetworkMount.getValue().getGateway());
                view.getNetworks().add(dockerContainerNetworkView);
            }
        }
        if (result.getHostConfig() != null && result.getHostConfig().getPortBindings() != null) {
            view.setPorts(new ArrayList<>());
            for (Map.Entry<String, List<DockerContainerInspectResult.HostConfig.PortBinding>> dockerContainerPort : result.getHostConfig().getPortBindings().entrySet()) {
                DockerContainerView.Port dockerContainerPortView = new DockerContainerView.Port();
                dockerContainerPortView.setContainerPort(dockerContainerPort.getKey());
                if (dockerContainerPort.getValue() != null && dockerContainerPort.getValue().size() > 0)
                dockerContainerPortView.setHostIp(dockerContainerPort.getValue().get(0).getHostIp());
                dockerContainerPortView.setHostPort(dockerContainerPort.getValue().get(0).getHostPort());
                view.getPorts().add(dockerContainerPortView);
            }
        }

        return view;
    }

    private DockerContainerCreateParam convert(DockerContainerParam param) {
        DockerContainerCreateParam result = new DockerContainerCreateParam();
        result.setImage(param.getImage());
        result.setHostConfig(new DockerContainerCreateParam.HostConfig());

        if (param.getCpu() != null && param.getCpu() != 0) {
            result.getHostConfig().setNanoCpus(param.getCpu());
        }
        if (param.getMemory() != null && param.getMemory() != 0) {
            result.getHostConfig().setMemory(param.getMemory() * 1024 * 1024 * 1024);
        }
        if (StringUtils.hasText(param.getHostname())) {
            result.setHostname(param.getHostname());
        }
        if (StringUtils.hasText(param.getRestartPolicy())) {
            result.getHostConfig().setRestartPolicy(new DockerContainerCreateParam.HostConfig.RestartPolicy());
            result.getHostConfig().getRestartPolicy().setName(param.getRestartPolicy());
        }
        if (Boolean.TRUE.equals(param.getPrivileged())) {
            result.getHostConfig().setPrivileged(param.getPrivileged());
        }
        if (StringUtils.hasText(param.getNetwork())) {
            DockerContainerCreateParam.NetworkingConfig.EndpointsConfig endpointsConfig = new DockerContainerCreateParam.NetworkingConfig.EndpointsConfig();
            if (StringUtils.hasText(param.getIpAddress()) && !"AUTO".equals(param.getIpAddress())) {
                endpointsConfig.setiPAMConfig(new DockerContainerCreateParam.NetworkingConfig.EndpointsConfig.IPAMConfig());
                endpointsConfig.getiPAMConfig().setiPv4Address(param.getIpAddress());
            }

            Map<String, DockerContainerCreateParam.NetworkingConfig.EndpointsConfig> endpointsConfigMap = new HashMap<>();
            endpointsConfigMap.put(param.getNetwork(), endpointsConfig);

            result.setNetworkingConfig(new DockerContainerCreateParam.NetworkingConfig());
            result.getNetworkingConfig().setEndpointsConfig(endpointsConfigMap);
        }
        if (StringUtils.hasText(param.getMacAddress()) && !"AUTO".equals(param.getMacAddress())) {
            result.setMacAddress(param.getMacAddress());
        }
        if (!CollectionUtils.isEmpty(param.getPorts())) {
            Map<String, List<DockerContainerCreateParam.HostConfig.PortBinding>> bindings = new HashMap<>();
            for (DockerContainerParam.Port port : param.getPorts()) {
                DockerContainerCreateParam.HostConfig.PortBinding binding = new DockerContainerCreateParam.HostConfig.PortBinding();
                if (StringUtils.hasText(port.getHostIp()) && !"AUTO".equals(port.getHostIp())) {
                    binding.setHostIp(port.getHostIp());
                }
                binding.setHostPort(port.getHostPort().toString());
                bindings.put(port.getContainerPort() + "/tcp", Collections.singletonList(binding));
            }
            result.getHostConfig().setPortBindings(bindings);
        }
        if (!CollectionUtils.isEmpty(param.getDns())) {
            result.getHostConfig().setDns(param.getDns());
        }
        if (!CollectionUtils.isEmpty(param.getHosts())) {
            result.getHostConfig().setExtraHosts(new ArrayList<>());
            for (DockerContainerParam.Hosts host : param.getHosts()) {
                result.getHostConfig().getExtraHosts().add(host.getUrl() + ":" + host.getIp());
            }
        }
        if (!CollectionUtils.isEmpty(param.getVolumes())) {
            result.getHostConfig().setBinds(new ArrayList<>());
            for (DockerContainerParam.Volume volume : param.getVolumes()) {
                if (StringUtils.hasText(volume.getMode())) {
                    result.getHostConfig().getBinds().add(volume.getSource() + ":" + volume.getDestination() + ":" + volume.getMode());
                } else {
                    result.getHostConfig().getBinds().add(volume.getSource() + ":" + volume.getDestination());
                }
            }
        }
        if (!CollectionUtils.isEmpty(param.getEnvs())) {
            result.setEnv(new ArrayList<>());
            for (DockerContainerParam.Env env : param.getEnvs()) {
                result.getEnv().add(env.getKey() + "=" + env.getValue());
            }
        }
        if (!CollectionUtils.isEmpty(param.getLabels())) {
            result.setLabels(new HashMap<>());
            for (DockerContainerParam.Label label : param.getLabels()) {
                result.getLabels().put(label.getKey(), label.getValue());
            }
        }
        if (StringUtils.hasText(param.getArg())) {
            result.setCmd(Arrays.asList(param.getArg().split(" ")));
        }
        return result;
    }

    private List<DockerContainerView> convert(DockerContainersResult result) {
        List<DockerContainerView> views = new ArrayList<>();
        for (DockerContainersResult.Container dockerContainerResult : result) {
            DockerContainerView view = new DockerContainerView();
            view.setId(dockerContainerResult.getId());
            view.setName(String.join(",", dockerContainerResult.getNames()));
            view.setCreated(dockerContainerResult.getCreatedDate());
            view.setImageId(dockerContainerResult.getImageID());
            view.setImageName(dockerContainerResult.getImage());
            view.setStatus(DockerContainerStatus.valueOf(dockerContainerResult.getState().toUpperCase()));
            if (dockerContainerResult.getMounts() != null) {
                view.setVolumes(new ArrayList<>());
                for (DockerContainersResult.Container.Mount dockerContainerMountResult : dockerContainerResult.getMounts()) {
                    DockerContainerView.Volume dockerContainerVolumeView = new DockerContainerView.Volume();
                    dockerContainerVolumeView.setSource(dockerContainerMountResult.getSource());
                    dockerContainerVolumeView.setDestination(dockerContainerMountResult.getDestination());
                    dockerContainerVolumeView.setMode(dockerContainerMountResult.getMode());
                    view.getVolumes().add(dockerContainerVolumeView);
                }
            }
            if (dockerContainerResult.getNetworkSettings() != null && dockerContainerResult.getNetworkSettings().getNetworks() != null) {
                view.setNetworks(new ArrayList<>());
                for (Map.Entry<String, DockerContainersResult.Container.NetworkSettings.Network> dockerContainerNetworkMount : dockerContainerResult.getNetworkSettings().getNetworks().entrySet()) {
                    DockerContainerView.Network dockerContainerNetworkView = new DockerContainerView.Network();
                    dockerContainerNetworkView.setNetworkId(dockerContainerNetworkMount.getValue().getNetworkID());
                    dockerContainerNetworkView.setNetworkName(dockerContainerNetworkMount.getKey());
                    dockerContainerNetworkView.setIpAddress(dockerContainerNetworkMount.getValue().getiPAddress());
                    dockerContainerNetworkView.setMacAddress(dockerContainerNetworkMount.getValue().getMacAddress());
                    dockerContainerNetworkView.setIpPrefixLen(dockerContainerNetworkMount.getValue().getiPPrefixLen());
                    dockerContainerNetworkView.setGateway(dockerContainerNetworkMount.getValue().getGateway());
                    view.getNetworks().add(dockerContainerNetworkView);
                }
            }
            if (dockerContainerResult.getPorts() != null) {
                view.setPorts(new ArrayList<>());
                for (DockerContainersResult.Container.Port dockerContainerPort : dockerContainerResult.getPorts()) {
                    DockerContainerView.Port dockerContainerPortView = new DockerContainerView.Port();
                    dockerContainerPortView.setHostIp(dockerContainerPort.getIp());
                    dockerContainerPortView.setHostPort(dockerContainerPort.getPublicPort());
                    dockerContainerPortView.setContainerPort(dockerContainerPort.getPrivatePort() + "/" + dockerContainerPort.getType());
                    view.getPorts().add(dockerContainerPortView);
                }
            }

            views.add(view);
        }
        return views;
    }

    public CommonPageResult<DockerContainerView> page(long pageSize, long pageNumber, String sort, String order, String serverId, String keyword) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerContainersResult dockerContainersResult = dockerClient.containers().all();
        List<DockerContainerView> dockerContainerViews = this.convert(dockerContainersResult);
        if (StringUtils.hasText(keyword)) {
            dockerContainerViews.removeIf(c -> !c.getName().contains(keyword));
        }
        dockerContainerViews.sort(Comparator.comparing(DockerContainerView::getCreated));
        int from = (int) (pageSize * (pageNumber - 1));
        int to = (int) (pageSize * pageNumber);
        to = Math.min(to, dockerContainerViews.size());
        return new CommonPageResult<>(dockerContainerViews.subList(from, to), dockerContainerViews.size());
    }

    public DockerContainerView get(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerContainerInspectResult dockerContainerInspectResult = dockerClient.containers().container(id).inspect();
        return this.convert(dockerContainerInspectResult);
    }

    public DockerContainerView create(String serverId, DockerContainerParam model) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerContainerCreateParam param = this.convert(model);
        DockerContainerCreateResult dockerContainerCreateResult = dockerClient.containers().create(model.getName(), param);
        DockerContainerView dockerContainerView = this.get(serverId, dockerContainerCreateResult.getId());
        eventPublisher.send(new WebSocketEvent(WebSocketEvent.Type.DOCKER_CONTAINER_REFRESH, "RESTART"));
        return dockerContainerView;
    }

    public void remove(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).remove();
    }

    public void start(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).start();
    }

    public void stop(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );

        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerContainerInspectResult dockerContainer = dockerClient.containers().container(id).inspect();

        DockerContainerStopJob.Param stdin = new DockerContainerStopJob.Param();
        stdin.setServer(dockerServer);
        stdin.setId(id);
        stdin.setName(dockerContainer.getName());

        UserView session = SecurityUtil.getSession();

        TaskParam taskParam = new TaskParam();
        taskParam.setType(TaskType.DOCKER_CONTAINER_STOP);
        taskParam.setStatus(TaskStatus.PENDING);
        taskParam.setName("停止容器");
        taskParam.setDescription("在宿主机[" + dockerServer.getName() + "]上停止容器[" + dockerContainer.getName() +"]");
        taskParam.setStdin(stdin.toJson());
        taskParam.setOwnerId(session.getId());

        TaskView taskResult = taskFeign.create(taskParam);

        try {
            scheduler.scheduleJob(
                    this.getJobDetail(taskResult.getId(), DockerContainerStopJob.class),
                    this.getJobTrigger(taskResult.getId(), DockerContainerStopJob.class)
            );
        } catch (SchedulerException e) {
            throw new IllegalStateException(e);
        }
    }

    public void restart(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );

        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());
        DockerContainerInspectResult dockerContainer = dockerClient.containers().container(id).inspect();

        DockerContainerRestartJob.Param stdin = new DockerContainerRestartJob.Param();
        stdin.setServer(dockerServer);
        stdin.setId(id);
        stdin.setName(dockerContainer.getName());

        UserView session = SecurityUtil.getSession();

        TaskParam taskParam = new TaskParam();
        taskParam.setType(TaskType.DOCKER_CONTAINER_RESTART);
        taskParam.setStatus(TaskStatus.PENDING);
        taskParam.setName("重启容器");
        taskParam.setDescription("在宿主机[" + dockerServer.getName() + "]上重启容器[" + dockerContainer.getName() +"]");
        taskParam.setStdin(stdin.toJson());
        taskParam.setOwnerId(session.getId());

        TaskView taskResult = taskFeign.create(taskParam);

        try {
            scheduler.scheduleJob(
                    this.getJobDetail(taskResult.getId(), DockerContainerRestartJob.class),
                    this.getJobTrigger(taskResult.getId(), DockerContainerRestartJob.class)
            );
        } catch (SchedulerException e) {
            throw new IllegalStateException(e);
        }
    }

    public void kill(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).kill();
    }

    public void pause(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).pause();
    }

    public void resume(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).unpause();
    }

    public List<String> log(String serverId, String id, long tail) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        return dockerClient.containers().container(id).log(tail);
    }

    public void rename(String serverId, String id, String name) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        DockerServer dockerServer = dockerServerDao.findById(serverId).orElseThrow(() ->
                new HttpException(HttpStatus.BAD_REQUEST, Constant.DOCKER_SERVER_NOT_FOUND)
        );
        DockerClient dockerClient = new DockerClient(dockerServer.getUrl());

        dockerClient.containers().container(id).rename(name);
    }

    private JobKey getJobKey(String id, Class<? extends Job> clazz) {
        return new JobKey(id, clazz.getName());
    }

    private JobDetail getJobDetail(String id, Class<? extends Job> clazz) {
        Map<String, Object> jobParam = new HashMap<String, Object>() {{
            this.put("id", id);
        }};
        return JobBuilder.newJob(clazz)
                .withIdentity(this.getJobKey(id, clazz))
                .usingJobData(new JobDataMap(jobParam))
                .build();
    }

    private Trigger getJobTrigger(String id, Class<? extends Job> clazz) {
        return TriggerBuilder.newTrigger()
                .withIdentity(id, clazz.getName())
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(1, 1))
                .build();
    }

    public DockerStatDonutView cpuDonut(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        Optional<DockerStat> stat = dockerStatDao.findFirstByServerIdAndContainerIdOrderByCreateDateDesc(serverId, id);

        BigDecimal used = stat.map(DockerStat::getCpuUsage).orElse(new BigDecimal(0));
        BigDecimal free = new BigDecimal(100).subtract(used);
        if (free.compareTo(new BigDecimal(0)) < 0) {
            free = new BigDecimal(0);
        }
        CssColor color = CssColor.SUCCESS;
        if (used.compareTo(new BigDecimal(90)) > 0) {
            color = CssColor.DANGER;
        } else if (used.compareTo(new BigDecimal(80)) > 0) {
            color = CssColor.WARNING;
        }

        DockerStatDonutView view = new DockerStatDonutView();
        view.setLabels(Arrays.asList("已用", "空闲"));
        DockerStatDonutView.DataSet dataSet = new DockerStatDonutView.DataSet();
        dataSet.setData(Arrays.asList(used, free));
        dataSet.setBackgroundColor(Arrays.asList(color.getRgb(), CssColor.LIGHT.getRgb()));
        view.setDatasets(Collections.singletonList(dataSet));

        return view;
    }

    public DockerStatDonutView memoryDonut(String serverId, String id) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        Optional<DockerStat> stat = dockerStatDao.findFirstByServerIdAndContainerIdOrderByCreateDateDesc(serverId, id);

        BigDecimal used = stat.map(DockerStat::getMemUsed).map(BigDecimal::new)
                .map(m -> m.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_EVEN))
                .orElse(new BigDecimal(0));
        BigDecimal total = stat.map(DockerStat::getMemLimit).map(BigDecimal::new)
                .map(m -> m.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_EVEN))
                .orElse(new BigDecimal(0));
        BigDecimal usage = stat.map(DockerStat::getMemUsage).orElse(new BigDecimal(0));
        BigDecimal free = total.subtract(used);
        CssColor color = CssColor.SUCCESS;
        if (usage.compareTo(new BigDecimal(90)) > 0) {
            color = CssColor.DANGER;
        } else if (usage.compareTo(new BigDecimal(80)) > 0) {
            color = CssColor.WARNING;
        }

        DockerStatDonutView view = new DockerStatDonutView();
        view.setLabels(Arrays.asList("已用", "空闲"));
        DockerStatDonutView.DataSet dataSet = new DockerStatDonutView.DataSet();
        dataSet.setData(Arrays.asList(used, free));
        dataSet.setBackgroundColor(Arrays.asList(color.getRgb(), CssColor.LIGHT.getRgb()));
        view.setDatasets(Collections.singletonList(dataSet));

        return view;
    }

    public DockerStatLineView cpuLine(String serverId, String id, Integer second, Boolean date) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        Calendar calendar = Calendar.getInstance();
        Date end = calendar.getTime();
        calendar.add(Calendar.SECOND, 0 - second);
        Date start = calendar.getTime();

        List<DockerStat> stats = dockerStatDao.findByServerIdAndContainerIdAndCreateDateBetween(serverId, id, start, end);
        SimpleDateFormat format = Boolean.TRUE.equals(date) ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") : new SimpleDateFormat("HH:mm:ss");

        DockerStatLineView view = new DockerStatLineView();
        view.setLabels(stats.stream().map(DockerStat::getCreateDate).map(format::format).collect(Collectors.toList()));
        DockerStatLineView.DataSet dataSet = new DockerStatLineView.DataSet();
        dataSet.setData(stats.stream().map(DockerStat::getCpuUsage).collect(Collectors.toList()));
        dataSet.setBackgroundColor(CssColor.LIGHT.getRgb());
        dataSet.setBorderColor(CssColor.INFO.getRgb());
        view.setDatasets(Collections.singletonList(dataSet));

        return view;
    }

    public DockerStatLineView memoryLine(String serverId, String id, Integer second, Boolean date) {
        Assert.notNull(serverId, "Param serverId can't be empty");
        Assert.notNull(id, "Param id can't be empty");

        Calendar calendar = Calendar.getInstance();
        Date end = calendar.getTime();
        calendar.add(Calendar.SECOND, 0 - second);
        Date start = calendar.getTime();

        List<DockerStat> stats = dockerStatDao.findByServerIdAndContainerIdAndCreateDateBetween(serverId, id, start, end);
        SimpleDateFormat format = Boolean.TRUE.equals(date) ? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") : new SimpleDateFormat("HH:mm:ss");

        DockerStatLineView view = new DockerStatLineView();
        view.setLabels(stats.stream().map(DockerStat::getCreateDate).map(format::format).collect(Collectors.toList()));
        DockerStatLineView.DataSet dataSet = new DockerStatLineView.DataSet();
        dataSet.setData(stats.stream().map(DockerStat::getMemUsed).map(BigDecimal::new)
                .map(m -> m.divide(new BigDecimal(1024 * 1024), 2, RoundingMode.HALF_EVEN)).collect(Collectors.toList()));
        dataSet.setBackgroundColor(CssColor.LIGHT.getRgb());
        dataSet.setBorderColor(CssColor.INFO.getRgb());
        view.setDatasets(Collections.singletonList(dataSet));

        return view;

    }
}
